/*
 *  Copright: 2008 Philipp Leitner, Distributed Systems Group,
 *            TU Wien, leitner@infosys.tuwien.ac.at
 *
 *  This file is part of DAIOS.
 *
 *  DAIOS is free software: you can redistribute it and/or modify
 *  it under the terms of the GNU Lesser General Public License as
 *  published by the Free Software Foundation.
 * 
 *  DAIOS is distributed in the hope that it will be useful,
 *  but WITHOUT ANY WARRANTY; without even the implied warranty of
 *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 *  GNU Lesser General Public License for more details.
 *
 *  You should have received a copy of the GNU Lesser General Public
 *  License along with DAIOS.  If not, see
 *  <http://www.gnu.org/licenses/>.
 * 
 */

package at.ac.tuwien.infosys.dsg.daios.wsdl.typesystem;

import java.util.Hashtable;

import org.apache.xmlbeans.SchemaGlobalElement;
import org.apache.xmlbeans.SchemaType;
import org.apache.xmlbeans.SchemaTypeSystem;

import at.ac.tuwien.infosys.dsg.daios.wsdl.impl.WSDLArrayType;
import at.ac.tuwien.infosys.dsg.daios.wsdl.impl.WSDLComplexType;
import at.ac.tuwien.infosys.dsg.daios.wsdl.impl.WSDLElementType;
import at.ac.tuwien.infosys.dsg.daios.wsdl.interfaces.IWSDLType;

/**
 * This type represents an entire type system from a WSDL
 * definition. The type system should be self-consistent
 * (i.e. all types used to define the global types in the
 * type system should be either built-in or also defined
 * within the type system).
 * 
 * <p>
 * 
 * The type system distinguishes between global <i>element</i>
 * types (i.e. top-level element tags) and 'regular' global
 * types (i.e. top-level type tags). Element types are
 * used in document invocation, while top-level types are used in
 * rpc-style.
 * 
 * @author philipp
 *
 */
public class WSDLTypeTree {
	
	private Hashtable<String, IWSDLType> globalTypes = null;
	private Hashtable<String, IWSDLType> globalElements = null;
	
	/**
	 * Construct a new empty type tree.
	 *
	 */
	private WSDLTypeTree(){
		globalTypes = new Hashtable<String,IWSDLType>();
		globalElements = new Hashtable<String,IWSDLType>();
	}
	
	/**
	 * The standard way of building up a new type system. Load the
	 * type system from a XMLBeans SchemaTypeSystem. When the type
	 * system is loaded entirely it is immutable.
	 * 
	 * <p>
	 * 
	 * Use this method to parse 'element types', i.e an doc-encoded
	 * WSDL file.
	 * 
	 * @param typeSystem An XMLBeans type system that should be converted
	 * into a Daios type tree.
	 * @return The Daios type tree.
	 */
	public static WSDLTypeTree parseElementTypeTree(SchemaTypeSystem typeSystem) {
		
		WSDLTypeTree tree = new WSDLTypeTree();
		
		// parse global elements (doc-style)
		SchemaGlobalElement[] els = typeSystem.globalElements();
		for(SchemaGlobalElement t: els) {
			IWSDLType newType = new WSDLElementType(t);
			tree.globalElements.put(newType.getName().getLocalPart(), newType);
		}
		
		return tree;
		
	}
	
	/**
	 * The standard way of building up a new type system. Load the
	 * type system from a XMLBeans SchemaTypeSystem. When the type
	 * system is loaded entirely it is immutable.
	 * 
	 * <p>
	 * 
	 * Use this method to parse 'global types', i.e an RPC-encoded
	 * WSDL file.
	 * 
	 * @param typeSystem An XMLBeans type system that should be converted
	 * into a Daios type tree.
	 * @return The Daios type tree.
	 */	
	public static WSDLTypeTree parseComplexTypeTree(SchemaTypeSystem typeSystem) {
		
		WSDLTypeTree tree = new WSDLTypeTree();
		
		// parse global types (rpc-style)
		SchemaType[] globalTypes = typeSystem.globalTypes();
		boolean arrayType = false;
		IWSDLType newType = null;
		for(SchemaType t: globalTypes) {
			arrayType = checkForArray(t);
			if(arrayType)
				newType = new WSDLArrayType(t.getName(), t);
			else
				newType = new WSDLComplexType(t.getName(), t);
			tree.globalTypes.put(newType.getTypeName().getLocalPart(), newType);
		}
		
		return tree;
		
	}
	
	/**
	 * Get a globally defined complex type by name.
	 * 
	 * @param name The name of the global complex type.
	 * @return The complex type.
	 */
	public IWSDLType getGlobalComplexType(String name) {
		return globalTypes.get(name);
	}
	
	/**
	 * Get a globally defined 'element type' (see above)
	 * by name.
	 * 
	 * @param name The name of the element type.
	 * @return The element type.
	 */
	public IWSDLType getGlobalElement(String name) {
		return globalElements.get(name);
	}	
	
	/**
	 * Check if a given type represents an array.
	 * 
	 * @param t The type to check.
	 * @return True if 't' represents an array, false otherwise.
	 */
	private static boolean checkForArray(SchemaType t) {
		// TODO: this is just abusing the Axis1 conventions!!!!!
		return t.getName().getLocalPart().startsWith("ArrayOf");
	}	
	
}
